﻿//Part of NUGET Package: pmunin.SystemExtensions
///Extentions shared with Silverlight project if it exists (This file automatically will be attached to SL project, in "Generated code" folder). If SL does not exist, file will be compiled only in the parent project
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;

public static partial class DictionaryExtensions
{
    /// <summary>
    /// Gets lazy value from dictionary or create it and put it in dictionary
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dictionary"></param>
    /// <param name="valueInitializer"></param>
    /// <param name="key"></param>
    /// <returns></returns>
    public static Lazy<T> GetAndCacheLazy<T>(this IDictionary dictionary, Func<T> valueInitializer, object key)
    {
        var fullKey = key;
        var value = dictionary[fullKey] as Lazy<T>;
        if (value == null)
        {
            dictionary[fullKey] = (value = new Lazy<T>(valueInitializer));
        }
        return value;
    }

    /// <summary>
    /// Try to gets value from dictionary, if value does not exist in dictionary it creates it and put it there by key
    /// </summary>
    /// <typeparam name="TDictionaryKey">key type of the dictionary</typeparam>
    /// <typeparam name="TDictionaryValue">value type of the dictionary</typeparam>
    /// <typeparam name="TResult">result of value to store in dictionary</typeparam>
    /// <param name="dictionary">dictionary to store cache at</param>
    /// <param name="valueInitializer">function that initialize the value to cache</param>
    /// <param name="key">key to store cache value by</param>
    /// <returns>cached value, or if it was not cached yet</returns>
    public static TResult GetAndCache<TDictionaryKey, TDictionaryValue, TResult>(this IDictionary<TDictionaryKey, TDictionaryValue> dictionary, Func<TResult> valueInitializer, TDictionaryKey key)
        where TResult : TDictionaryValue
    {
        var res = default(TDictionaryValue);
        if (!dictionary.TryGetValue(key, out res))
        {
            dictionary[key] = res = valueInitializer();
        }
        return (TResult)res;
    }

    //public static T GetAndCache<T>(this IDictionary dictionary, Func<T> valueInitializer, object key)
    //{
    //    var v = dictionary.GetAndCacheLazy(valueInitializer, key);
    //    return v.Value;
    //}

}

namespace System
{
    public interface IIndexerGetter<TKey, TValue>
    {
        TValue this[TKey key] { get; }
    }

    public class DictionaryIndexer<TKey, TValue> : IIndexerGetter<TKey, TValue>
    {
        IDictionary<TKey, TValue> source;

        public DictionaryIndexer(IDictionary<TKey, TValue> source)
        {
            this.source = source;
        }
        public TValue this[TKey key]
        {
            get
            {
                return source[key];
            }
        }
    }

    /// <summary>
    /// Allow to address objects by index via function delegate
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public class FuncIndexer<TKey, TValue> : IIndexerGetter<TKey, TValue>
    {
        Func<TKey, TValue> valueFactory;
        public FuncIndexer(Func<TKey, TValue> valueFactory)
        {
            this.valueFactory = valueFactory;
        }

        public TValue this[TKey key]
        {
            get { return valueFactory(key); }
        }
    }

    /// <summary>
    /// Object will execute delegate onDispose, can be used for scoped entities via "using" keyword
    /// </summary>
    public class DisposableScope : IDisposable
    {
        public DisposableScope(Action onStart, Action onDispose)
        {
            OnStart = onStart;
            OnDispose = onDispose;
            if (OnStart != null) OnStart();
        }
        public DisposableScope(Action onDispose)
            : this(null, onDispose)
        {
        }

        public Action OnStart { get; set; }

        public Action OnDispose { get; set; }

        public void Dispose()
        {
            if (OnDispose != null) OnDispose();
        }
    }
}

public static class ReflectionExtensions
{
    /// <summary>
    /// Retrieves reflection Member by simple Lambda expression
    /// </summary>
    /// <typeparam name="TThis"></typeparam>
    /// <typeparam name="TRes"></typeparam>
    /// <param name="obj"></param>
    /// <param name="memberExpression"></param>
    /// <returns></returns>
    public static MemberInfo GetMember<TThis, TRes>(this TThis obj, Expression<Func<TThis, TRes>> memberExpression)
    {
        if (memberExpression.Body.NodeType == ExpressionType.MemberAccess)
        {
            var memberExprBody = memberExpression.Body as MemberExpression;
            if (memberExprBody != null)
                return memberExprBody.Member;
        }
        throw new NotSupportedException(string.Format("{0} is not valid member expression", memberExpression));
    }

    /// <summary>
    /// Get reflection Member by simple lambda expression
    /// </summary>
    /// <typeparam name="TThis"></typeparam>
    /// <typeparam name="TRes"></typeparam>
    /// <param name="obj"></param>
    /// <param name="memberExpression"></param>
    /// <returns></returns>
    public static MemberInfo GetMember<TThis, TRes>(this TThis obj, Expression<Func<TRes>> memberExpression)
    {
        if (memberExpression.Body.NodeType == ExpressionType.MemberAccess)
        {
            var memberExprBody = memberExpression.Body as MemberExpression;
            if (memberExprBody != null)
                return memberExprBody.Member;
        }
        throw new NotSupportedException(string.Format("{0} is not valid member expression", memberExpression));
    }

    /// <summary>
    /// Retrieves name as a string by simple Lambda expression
    /// </summary>
    /// <typeparam name="TThis"></typeparam>
    /// <typeparam name="TRes"></typeparam>
    /// <param name="obj"></param>
    /// <param name="memberExpression"></param>
    /// <returns></returns>
    public static string GetMemberName<TThis, TRes>(this TThis obj, Expression<Func<TThis, TRes>> memberExpression)
    {
        var m = GetMember(obj, memberExpression);
        return m.Name;
    }

    /// <summary>
    /// Retrieves name as a string by simple Lambda expression
    /// </summary>
    /// <typeparam name="TThis"></typeparam>
    /// <typeparam name="TRes"></typeparam>
    /// <param name="obj"></param>
    /// <param name="memberExpression"></param>
    /// <returns></returns>
    public static string GetMemberName<TThis, TRes>(this TThis obj, Expression<Func<TRes>> memberExpression)
    {
        return GetMember(obj, memberExpression).Name;
    }
}